home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Utilities / Winter Shell 1.0d2 / Source / Libraries / HelpLib / HelpLib.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-16  |  8.8 KB  |  354 lines  |  [TEXT/KAHL]

  1. /* Display a list of help topics next to help text. The user can choose
  2.     a topic from the list. The list and text can both used styled text.
  3.     The list of topics are contained, one per line, in the resources with
  4.     id RLHELP_TOPICS of type RES_TEXT_TYPE and RES_STYL_TYPE. The text
  5.     for each topic is contained in resources of type RES_TEXT_TYPE and
  6.     RES_STYL_TYPE with the same name as the topic (so GetNamedResource
  7.     can be used to load the text corresponding to the topic).
  8.     
  9.     94/01/12 aih
  10.     - overrides list's within handler, rather than relying on the order
  11.     in which event handlers are executed
  12.     
  13.     93/12/16 aih
  14.     - window's position is saved in prefs file
  15.     
  16.     93/11/17 aih
  17.     - added resize function to minimize drawing drawn on resizing list
  18.     
  19.     93/03/17 AIH
  20.     - Topic is updated on null events
  21.     
  22.     93/03/10 AIH
  23.     - The window's minimum and maximum size are set to fix a problem with
  24.     zooming the window
  25.     
  26.     93/03/06 AIH
  27.     - Up and down arrow keys scroll can be used to select topics
  28.     - All help windows are closed when memory is low
  29.     - Menu commands are disabled if memory is low
  30.     
  31.     93/03/03 Ari Halberstadt (AIH)
  32.     - Created */
  33.  
  34. #include <ctype.h>
  35. #include <limits.h>
  36. #include <string.h>
  37. #include "DialogLib.h"
  38. #include "DrawLib.h"
  39. #include "EventLib.h"
  40. #include "HelpLib.h"
  41. #include "KeyLib.h"
  42. #include "ListLib.h"
  43. #include "MathLib.h"
  44. #include "MemoryLib.h"
  45. #include "PreferencesLib.h"
  46. #include "RectangleLib.h"
  47. #include "ResourceConstantsLib.h"
  48. #include "ResourceLib.h"
  49. #include "ResourceTypeLib.h"
  50. #include "TextIOLib.h"
  51. #include "WindowLib.h"
  52.  
  53. /* items in the help dialog */
  54. enum {
  55.     iListUser = 1,
  56.     iTextUser
  57. };
  58.  
  59. static HelpHandle gHelp;
  60.  
  61. /* true if the help window is valid */
  62. Boolean HelpValid(HelpHandle help)
  63. {
  64.     return(HandleValidSize(help, sizeof(HelpType)));
  65. }
  66.  
  67. /* true if the help window topic is valid */
  68. Boolean HelpTopicValid(HelpHandle help, HelpTopicType topic)
  69. {
  70.     Rect bounds;
  71.     ListDataBounds((**help).list, &bounds);
  72.     return((bounds.top == bounds.bottom && topic == bounds.top) ||
  73.                 (bounds.top <= topic && topic < bounds.bottom));
  74. }
  75.  
  76. /* read the list of topics */
  77. static void HelpReadList(HelpHandle help)
  78. {
  79.     Str255 str;
  80.     Cell cell;
  81.     short n = 0;
  82.         
  83.     require(HelpValid(help));
  84.     n = *(short *) *ResGet('STR#', RLS_HELP);
  85.     LDoDraw(false, (**help).list);
  86.     LAddRow(n, 0, (**help).list);
  87.     for (cell.h = cell.v = 0; cell.v < n; cell.v++) {
  88.         GetIndString(str, RLS_HELP, cell.v + 1);
  89.         LSetCell(str + 1, *str, cell, (**help).list);
  90.     }
  91.     LDoDraw(true, (**help).list);
  92.     ensure(HelpValid(help));
  93. }
  94.  
  95. /* Read the help corresponding to the indexed topic. A missing style
  96.     resource does not prevent the help system from working. */
  97. static void HelpReadText(HelpHandle help, HelpTopicType topic)
  98. {
  99.     volatile TextHandle text = NULL;    /* the text record */
  100.     Handle    rsrc = NULL;/* handle to the text resource */
  101.     short        id = 0;        /* id of the resource */
  102.     OSType    type = 0;    /* type of the resource */
  103.     Str255    dummy;        /* name of resource (name of topic as Pascal string) */
  104.     Cell        cell;            /* cell containing topic name */
  105.     CStr255    name;            /* name of topic to load */
  106.     short        len = 0;        /* length of topic name */
  107.     
  108.     require(HelpValid(help));
  109.     require(HelpTopicValid(help, topic));
  110.     TRY {
  111.         len = sizeof(name)-1; cell.h = 0; cell.v = topic;
  112.         LGetCell(name, &len, cell, (**help).list);
  113.         name[len] = 0;
  114.         text = TxScrlText((**help).text);
  115.         rsrc = ResGetNamed(RES_HELP_TYPE, name);
  116.         GetResInfo(rsrc, &id, &type, dummy);
  117.         FailResError();
  118.         TxReadTextRes(text, RES_HELP_TYPE, id);
  119.         if (ResExists('styl', id))
  120.             TxReadStyl(text, id);
  121.         TxInval(text);
  122.         TxSelect(text, 0, 0);
  123.         TxScrlChanged((**help).text);
  124.     } CATCH {
  125.         if (text) {
  126.             TxSelect(text, 0, TxLength(text));
  127.             TxDelete(text);
  128.             TxScrlChanged((**help).text);
  129.         }
  130.     } ENDTRY;
  131.     ensure(HelpValid(help));
  132. }
  133.  
  134. /* begin use of a help window */
  135. HelpHandle HelpBegin(void)
  136. {
  137.     static EventTableType overrideTable; /* for overriding list's within handler */
  138.     volatile HelpHandle help = NULL;    /* the new help structure */
  139.     TextScrollHandle text = NULL;        /* field for displaying help text */
  140.     ListHandle list = NULL;                /* displays list of help topics */
  141.     DialogPtr dlg = NULL;                /* dialog containing help */
  142.     Rect lbounds;                            /* list's data bounds */
  143.     Cell lsize;                                /* size of list's cells */
  144.     Rect box;                                /* for setting bounds of list and text */
  145.     
  146.     TRY {
  147.     
  148.         /* create dialog */
  149.         help = HandleBeginClear(sizeof(HelpType));
  150.         dlg = DlgBegin(RLD_HELP);
  151.         (**help).dlg = dlg;
  152.         DlgCenter(dlg);
  153.         
  154.         /* create text */
  155.         DlgBox(dlg, iTextUser, &box);
  156.         text = TxScrlBegin(dlg, &box, true, true, false, false,
  157.                     TX_UNSTYLED_KIND, NULL);
  158.         (**help).text = text;
  159.         TxMarginSet(TxScrlText(text), 0);
  160.         TxWrapSet(TxScrlText(text), true);
  161.         TxFlagsSet(TxScrlText(text),
  162.             TxFlags(TxScrlText(text)) | TX_READ_ONLY | TX_WRAP_TO_VIEW);
  163.         
  164.         /* create list */
  165.         SetPt(&lsize, 0, 0);
  166.         SetRect(&lbounds, 0, 0, 1, 0);
  167.         DlgBox(dlg, iListUser, &box);
  168.         list = ListBegin(&box, &lbounds, lsize, 0, dlg,
  169.                                 true, false, false, true, true);
  170.         (**help).list = list;
  171.         (**list).selFlags = lOnlyOne;
  172.         FrameFlagsSet(ListFrame(list), FrameFlags(ListFrame(list)) & ~FRAME_ANCHOR_RIGHT);
  173.  
  174.         /* replace list's within handler so it isn't called, which allows
  175.             us to catch the mouse down events in the list and make it look
  176.             like they were clicks in the help object */
  177.         overrideTable = *WinObjectTable(dlg, list);
  178.         overrideTable.focusWindow.within = NULL;
  179.         WinObjectTableSet(dlg, list, &overrideTable);
  180.         
  181.         /* read help */
  182.         HelpReadList(help);
  183.         HelpTopicSet(help, 0);
  184.  
  185.         /* finish setting up window */
  186.         WinRegister(dlg, help, HelpEventTable());
  187.         WinTabUnregister(dlg, list);
  188.         WinTabRegister(dlg, help);
  189.         WinFocusSet(dlg, help);
  190.         WinZoomRestore(dlg, PrefsFile(), PREFS_HELP_POSITION);
  191.         WinShow(dlg);
  192.         
  193.     } CATCH {
  194.         HelpEnd(help);
  195.     } ENDTRY;
  196.     ensure(HelpValid(help));
  197.     return(help);
  198. }
  199.  
  200. /* end use of the help window */
  201. void HelpEnd(HelpHandle help)
  202. {
  203.     if (help) {
  204.         WinTabUnregister((**help).dlg, help);
  205.         WinUnregister((**help).dlg, help);
  206.         TxScrlEnd((**help).text);
  207.         ListEnd((**help).list);
  208.         DlgEnd((**help).dlg);
  209.         HandleEnd(help);
  210.         help = NULL;
  211.     }
  212.     ensure(! HelpValid(help));
  213. }
  214.  
  215. /* return the index to the topic displayed in the help */
  216. HelpTopicType HelpTopic(HelpHandle help)
  217. {
  218.     require(HelpValid(help));
  219.     return((**help).topic);
  220. }
  221.  
  222. /* set the topic displayed in the help window */
  223. void HelpTopicSet(HelpHandle help, short topic)
  224. {
  225.     Cell cell;
  226.     Boolean sel = false;
  227.     
  228.     require(HelpValid(help));
  229.     require(HelpTopicValid(help, topic));
  230.     (**help).topic = (**help).newtopic = topic;
  231.     cell.h = 0; cell.v = topic;
  232.     ListSelectOne((**help).list, cell);
  233.     LAutoScroll((**help).list);
  234.     HelpReadText(help, topic);
  235.     ensure(HelpValid(help));
  236. }
  237.  
  238. /* true if the point is within the topic list */
  239. Boolean HelpWithin(HelpHandle help, Point where)
  240. {
  241.     require(HelpValid(help));
  242.     return(ListWithin((**help).list, where));
  243. }
  244.  
  245. /* set input focus */
  246. void HelpFocus(HelpHandle help, Boolean focus)
  247. {
  248.     ListFocus((**help).list, focus);
  249. }
  250.  
  251. /* update topic */
  252. void HelpIdle(HelpHandle help)
  253. {
  254.     require(HelpValid(help));
  255.     if ((**help).newtopic != (**help).topic)
  256.         HelpTopicSet(help, (**help).newtopic);
  257. }
  258.  
  259. /* return sleep period */
  260. TicksType HelpAdjustSleep(HelpHandle help)
  261. {
  262.     return(help && (**help).newtopic != (**help).topic ? 0 : LONG_MAX);
  263. }
  264.  
  265. /* select a topic if it's an arrow key */
  266. void HelpKeyDown(HelpHandle help, EventRecord *event)
  267. {
  268.     Cell cell;
  269.         
  270.     require(HelpValid(help));
  271.     ListKeyDown((**help).list, event);
  272.     cell.h = cell.v = 0;
  273.     if (LGetSelect(true, &cell, (**help).list))
  274.         (**help).newtopic = cell.v;
  275.     ensure(HelpValid(help));
  276. }
  277.  
  278. /* select a topic */
  279. void HelpMouseDown(HelpHandle help, EventRecord *event)
  280. {
  281.     Cell cell;
  282.     
  283.     require(HelpValid(help));
  284.     ListMouseDown((**help).list, event);
  285.     cell.h = cell.v = 0;
  286.     if (LGetSelect(true, &cell, (**help).list))
  287.         (**help).newtopic = cell.v;
  288.     ensure(HelpValid(help));
  289. }
  290.  
  291. /* true if help has the focus */
  292. static Boolean HelpHasFocus(void)
  293. {
  294.     return(gHelp && WinHasFocus((**gHelp).dlg));
  295. }
  296.  
  297. /* enable the help menu item if the help window isn't the front window */
  298. void HelpAdjustMenu(void)
  299. {
  300.     if (MemWarning() < MEM_WARNING_VERY_LOW) {
  301.         if (! WinModalHasFocus()) MenuCmdEnable(CMD_HELP);
  302.         MenuCmdCheck(CMD_HELP, HelpHasFocus());
  303.     }
  304.     if (HelpHasFocus())
  305.         MenuCmdEnable(CMD_CLOSE);
  306. }
  307.  
  308. /* open or select the main help window */
  309. void HelpOpen(void)
  310. {
  311.     if (gHelp)
  312.         WinSelect((**gHelp).dlg);
  313.     else
  314.         gHelp = HelpBegin();
  315. }
  316.  
  317. /* close the main help window */
  318. void HelpClose(void)
  319. {
  320.     if (gHelp) {
  321.         if ((**gHelp).dlg)
  322.             WinZoomSave((**gHelp).dlg, PrefsFile(), PREFS_HELP_POSITION);
  323.         HelpEnd(gHelp);
  324.         gHelp = NULL;
  325.     }
  326. }
  327.  
  328. /* handle the help menu command */
  329. Boolean HelpMenu(const MenuPickType *pick)
  330. {
  331.     Boolean handled = false;
  332.     
  333.     switch (pick->cmd) {
  334.     case CMD_HELP:
  335.         HelpOpen();
  336.         handled = true;
  337.         break;
  338.     case CMD_CLOSE:
  339.         if (HelpHasFocus()) {
  340.             HelpClose();
  341.             handled = true;
  342.         }
  343.         break;
  344.     }
  345.     return(handled);
  346. }
  347.  
  348. /* handle a low memory situation */
  349. void HelpMemoryLow(void)
  350. {
  351.     if (gHelp && MemWarning() >= MEM_WARNING_VERY_LOW)
  352.         HelpClose();
  353. }
  354.